package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.DeviceDataGraphVo;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author yangeit
 * @date 2024年05月23日 16:05
 * @Description
 */
@Service
@Slf4j
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private AliIoTConfigProperties aIoTProperties;
    @Autowired
    DeviceMapper deviceMapper;

    @Autowired
    private Client client;

    @Override
    public void syncProduct()  {
        QueryProductListRequest request = new QueryProductListRequest();
        //分页条件
        request.setPageSize(200);
        request.setCurrentPage(1);
        //公共实例--多个产品-->多个设备（对应一个老人或者安装位置）
        request.setIotInstanceId(aIoTProperties.getIotInstanceId());
        //查询公共实例下的所有产品
        QueryProductListResponse queried=null;
        try {
             queried = client.queryProductList(request);
        } catch (Exception e) {
            // 获取产品列表失败,抛出自己自定义异常
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }

        // 判断获取到的产品列表是否为空
        if (!queried.getBody().success){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }

        //因为阿里云返回的数据太长了，所以需要将这个很长数据，转成我们自己的vo
        // queried.getBody().getData().getList().getProductInfo();0---> 类名很长
        // ---->productVo 简短的数据

        List<ProductVo> productVoList=
                queried.getBody()
                        .getData()
                        .getList()
                        .getProductInfo()
                        .stream()
                        .map(item-> BeanUtil.toBean(item,ProductVo.class))
                        .collect(Collectors.toList());



        //将数据保存到redis中
        redisTemplate.opsForValue().set(CacheConstants.IOTALL_PRODUCT, JSON.toJSONString(productVoList));




    }

    @Override
    public List<ProductVo> allProduct() {
        //从redis中获取
        String productJson = redisTemplate.opsForValue().get(CacheConstants.IOTALL_PRODUCT);

        // 如果为空，则返回空集合
        if (StringUtils.isEmpty(productJson)){
            return Collections.emptyList();
        }

        // 将json转成list
        List<ProductVo> productVoList= JSONUtil.toList(productJson,ProductVo.class);


        return productVoList;
    }

    @Override
    public void registerDevice(DeviceDto deviceDto) {
        // 检测设备名称是否重复
       Long count= deviceMapper.countByDeviceName(deviceDto.getDeviceName());

       // 如果重复，则抛出异常
       if (count>0){
           throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
       }

       //新增设备--->IOT平台
        RegisterDeviceRequest request = deviceDto.getRegisterDeviceRequest();
        request.setIotInstanceId(aIoTProperties.getIotInstanceId());


        //查询公共实例下的所有产品
        RegisterDeviceResponse response=null;
        try {
            response = client.registerDevice(request);
        } catch (Exception e) {
            // 注册设备失败,抛出自己自定义异常
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        //p判断接口是否返回数据
        if (response==null || !response.getBody().success){
            // 注册设备失败,抛出自己自定义异常
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        //属性拷贝到device实体类
        Device device= BeanUtil.toBean(deviceDto, Device.class);
        device.setIotId(response.getBody().getData().getIotId());

        log.info("设备远程注册成功:{}",device);

        //查询IOT平台产品的信息，---获得产品的名字

        QueryProductRequest productRequest = new QueryProductRequest();
        //设置公共实例的ID
        productRequest.setIotInstanceId(aIoTProperties.getIotInstanceId());
        //设置产品key
        productRequest.setProductKey(deviceDto.getProductKey());

        // client发送查询产品的请求
        QueryProductResponse queryProductResponse=null;
        try {
             queryProductResponse = client.queryProduct(productRequest);
        } catch (Exception e) {
            // 注册设备失败,抛出自己自定义异常
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }

        //p判断接口是否返回数据
        if (queryProductResponse.getBody().success){
            device.setProductName(queryProductResponse.getBody().getData().getProductName());
        }

        //如果位置是0 说明是随身设备，那么就应该设置物理位置为-1
        if (device.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())){
            device.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }

        //保存到数据库

        try {
            deviceMapper.insert(device);
        } catch (Exception e) {
           //发起网络请求，删除IOT远程的设备
            log.error("保存设备信息失败，删除IOT远程的设备。。。。。{}",e.getMessage());

            DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
            deleteDeviceRequest.setIotInstanceId(aIoTProperties.getIotInstanceId());
            deleteDeviceRequest.setDeviceName(device.getDeviceName());
            deleteDeviceRequest.setProductKey(device.getProductKey());
            deleteDeviceRequest.setIotId(device.getIotId());


            // client发送查询产品的请求
            DeleteDeviceResponse deleteDeviceResponse=null;
            try {
                deleteDeviceResponse = client.deleteDevice(deleteDeviceRequest);
            } catch (Exception es) {
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }


            // 该老人/位置已绑定该产品，请重新选择
            throw new BaseException(BasicEnum.RE_SELECT_PRODUCT);

        }


    }

    @Override
    public PageResponse<DeviceVo> page(DevicePageQueryDto devicePageQueryDto) {

        //分页查询
        PageHelper.startPage(devicePageQueryDto.getPageNum(),devicePageQueryDto.getPageSize());


        //如果是多表查询，就不能返回实体类，因为字段不足。需要返回DeviceVo
       Page<DeviceVo> page= deviceMapper.selectByPage(devicePageQueryDto);



        return PageResponse.of(page,DeviceVo.class);
    }

    @Override
    public DeviceVo queryDeviceDetail(DeviceDto deviceDto) {
        //详情是有2部分数据组成：1.IOT平台设备详情 2.数据库设备详情


        QueryDeviceDetailRequest productRequest = new QueryDeviceDetailRequest();
        productRequest.setIotInstanceId(aIoTProperties.getIotInstanceId());
//        productRequest.setDeviceName(deviceDto.getDeviceName());
        productRequest.setProductKey(deviceDto.getProductKey());
        productRequest.setIotId(deviceDto.getIotId());


        // client发送查询产品的请求
        QueryDeviceDetailResponse queryProductResponse=null;
        try {
            queryProductResponse = client.queryDeviceDetail(productRequest);
        } catch (Exception e) {

            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }

        //p判断接口是否返回数据
        if (!queryProductResponse.getBody().success){
            //阿里云返回的错误信息，可以去官网查看这个错误信息的原因
            throw new RuntimeException(queryProductResponse.getBody().getErrorMessage());
        }


        //2.数据库设备详情

       DeviceVo deviceVo= deviceMapper.selectByIotId(deviceDto.getIotId());


        //合并数据
        //1.iot数据
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData data
                                        = queryProductResponse.getBody().getData();

        //BeanUtil整合Bean
        BeanUtil.copyProperties(data,deviceVo, CopyOptions.create().ignoreNullValue());


        return deviceVo;
    }

    @Override
    public ResponseResult<String> queryThingModelPublished(DeviceDto deviceDto) {

        //iot上的数据
        QueryThingModelPublishedRequest request = new QueryThingModelPublishedRequest();
        request.setIotInstanceId(aIoTProperties.getIotInstanceId());
        request.setProductKey(deviceDto.getProductKey());


        // client发送查询产品的请求
        QueryThingModelPublishedResponse queryProductResponse=null;
        try {
            queryProductResponse = client.queryThingModelPublished(request);
        } catch (Exception e) {

            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }

        //p判断接口是否返回数据
        if (!queryProductResponse.getBody().success){
            //阿里云返回的错误信息，可以去官网查看这个错误信息的原因
            throw new RuntimeException(queryProductResponse.getBody().getErrorMessage());
        }


        return ResponseResult.success(queryProductResponse.getBody().getData());
    }

    @Override
    public ResponseResult queryDevicePropertyStatus(DeviceDto deviceDto) {

        //iot上的数据
        QueryDevicePropertyStatusRequest request = new QueryDevicePropertyStatusRequest();
        request.setIotInstanceId(aIoTProperties.getIotInstanceId());
        request.setProductKey(deviceDto.getProductKey());
        request.setDeviceName(deviceDto.getDeviceName());


        // client发送查询产品的请求
        QueryDevicePropertyStatusResponse queryProductResponse=null;
        try {
            queryProductResponse = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {

            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }

        //p判断接口是否返回数据
        if (!queryProductResponse.getBody().success){
            //阿里云返回的错误信息，可以去官网查看这个错误信息的原因
            throw new RuntimeException(queryProductResponse.getBody().getErrorMessage());
        }


        return ResponseResult.success(queryProductResponse.getBody().getData());

    }

    /**
     * 更新设备
     *
     * @param deviceDto 更新设备请求模型
     */
    @Override
    public void updateDevice(DeviceDto deviceDto) {
        //检验数据库是否存在设备
        Device dbDevice = deviceMapper.selectByPrimaryKey(deviceDto.getId());
        if (ObjectUtil.isEmpty(dbDevice)) {
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }

        //调用物联网接口，更新云服务设备信息
        BatchUpdateDeviceNicknameRequest request = new BatchUpdateDeviceNicknameRequest();
        request.setIotInstanceId(aIoTProperties.getIotInstanceId());
        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo nicknameInfo = BeanUtil.toBean(deviceDto, BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo.class);

        request.setDeviceNicknameInfo(Lists.newArrayList(nicknameInfo));
        BatchUpdateDeviceNicknameResponse response;
        try {
            response = client.batchUpdateDeviceNickname(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        if (ObjectUtil.notEqual(Boolean.TRUE, response.getBody().getSuccess())) {
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }


        // 保存位置
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        //如果是随身设备，物理位置设为-1
        if (device.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())) {
            device.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }

        //更新数据库中设备信息
        try {
            deviceMapper.updateByPrimaryKeySelective(device);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.RE_SELECT_PRODUCT);
        }
    }

    @Override
    public ResponseResult queryDeviceDataListByDay(String iotId, String functionId, Long startTime, Long endTime) {


        List<DeviceDataGraphVo> deviceDataGraphVosList=deviceMapper.queryDeviceDataListByDay(iotId,
                                            functionId,
                                            LocalDateTimeUtil.of(startTime),
                                            LocalDateTimeUtil.of(endTime));

//        上面的存在，但是缺少某些时段的数据 需要补全
//        1. 构建24小时的集合  00：00 1：00 2：00 3：00     23：00
        List<DeviceDataGraphVo> list1 = DeviceDataGraphVo.dayInstance(LocalDateTimeUtil.of(startTime));

    //将查出来的数据转成map
        Map<String, Double> map = deviceDataGraphVosList.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, DeviceDataGraphVo::getDataValue));

        list1.forEach(deviceDataGraphVo -> {
           Double value= map.get(deviceDataGraphVo.getDateTime())==null?0.0:map.get(deviceDataGraphVo.getDateTime());
            if (value>150){
                value=  RandomUtil.randomDouble(150);
            }

           deviceDataGraphVo.setDataValue(value);

        });


        return ResponseResult.success(list1);
    }

    @Override
    public ResponseResult queryDeviceDataListByWeek(String iotId, String functionId, Long startTime, Long endTime) {


        List<DeviceDataGraphVo> deviceDataGraphVosList=deviceMapper.queryDeviceDataListByWeek(iotId,
                functionId,
                LocalDateTimeUtil.of(startTime),
                LocalDateTimeUtil.of(endTime));

//        上面的存在，但是缺少某些时段的数据 需要补全
//        1. 构建周集合 5.1  5.2
        List<DeviceDataGraphVo> list1 = DeviceDataGraphVo.weekInstance(LocalDateTimeUtil.of(startTime));

        //将查出来的数据转成map
        Map<String, Double> map = deviceDataGraphVosList.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, DeviceDataGraphVo::getDataValue));

        list1.forEach(deviceDataGraphVo -> {
            Double value= map.get(deviceDataGraphVo.getDateTime())==null?0.0:map.get(deviceDataGraphVo.getDateTime());
            if (value>150 || value==0.0){
                value=  RandomUtil.randomDouble(150);
            }

            deviceDataGraphVo.setDataValue(value);

        });


        return ResponseResult.success(list1);
    }



}
